/*------------------------------------------------------------------------------*\
**   Copyright 2008-2009 Jonhnny Weslley                                        **
**                                                                              **
**   Licensed under the Apache License, Version 2.0 (the "License");            **
**   you may not use this file except in compliance with the License.           **
**   You may obtain a copy of the License at                                    **
**                                                                              **
**       http://www.apache.org/licenses/LICENSE-2.0                             **
**                                                                              **
**   Unless required by applicable law or agreed to in writing, software        **
**   distributed under the License is distributed on an "AS IS" BASIS,          **
**   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   **
**   See the License for the specific language governing permissions and        **
**   limitations under the License.                                             **
\*------------------------------------------------------------------------------*/

package org.scalashell

import jline.{Completor, NullCompletor, SimpleCompletor, ArgumentCompletor}
import ShellSystem.{messages => %}

trait Command {

  def name(): String

  def completor(): Completor

  def execute(args: List[String])

}


class CommandException(val command: Command, message: String, cause: Throwable) extends Exception(message, cause)


abstract class CommandSupport(val name: String) extends Command {

  type CommandFunction = List[String] => Unit

  def completor = {
    val completorList = new java.util.ArrayList[Completor]
    if (name ne null) {
      completorList.add(new SimpleCompletor(name))
    }

    createCompletors match {
      case Some(completors) => completors.foreach(completorList.add(_))
      case None =>
    }

    new ArgumentCompletor(completorList)
  }

  protected def createCompletors: Option[List[Completor]]

  def fail(message: String) = throw new CommandException(this, message, null)

  def fail(message: String, cause: Throwable) = throw new CommandException(this, message, cause)

  override def toString = %("scalashell.command.to_string", name)

}

class SimpleCommandSupport(name: String) extends CommandSupport(name) {

  private var function: CommandFunction = {args => }

  def execute(args: List[String]) = {
    function(args)
  }

  def execute(codeBlock: CommandFunction) = {
    function = codeBlock
  }

  protected def createCompletors: Option[List[Completor]] = None

}

class ComplexCommandSupport(name: String, defaultFunction: Option[String]) extends CommandSupport(name) {

  import scala.collection.mutable.Map

  private val functionMap = Map[String, CommandFunction]()

  def this(name: String) = this(name, None)

  def this(name: String, default: String) = this(name, Some(default))

  def function(name: String)(body: CommandFunction) = {
    functionMap(name) = body
  }

  implicit def functionByName(name: String) = new {

    def | (body: CommandFunction) = function(name)(body)

  }

  protected def createCompletors: Option[List[Completor]] = {
    val cmdCompletor = new SimpleCompletor(new Array[String](0))
    functionMap.keys foreach (cmdCompletor addCandidateString _)
    Some(cmdCompletor :: Nil)
  }

  def execute(args: List[String]) = {
    var fArgs = args
    if (args.isEmpty) {
      defaultFunction match {
        case Some(default) => fArgs = (default :: Nil)
        case None => fail("Command '" + name + "' requires at least one argument")
      }
    }
    executeFunction(fArgs)
  }

  private def executeFunction(args: List[String]) = {
    val functionName = args(0)
    functionMap get functionName match {
      case Some(function) => {
        val fArgs = if (args.length > 1) args.drop(1) else Nil
        function(fArgs)
      }
      case None => fail("Unknown function: " + functionName)
    }
  }

  override def toString() = super.toString + "\n\n" +
        functionMap.keys.foldLeft(%("help.options")) {
          (options, function) => options + "\n    " + %("help.format.function", function, name)
        }

}